package com.crypto.impl;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import com.crypto.Cryptographer;
import com.crypto.exception.CryptographyException;
import com.crypto.exception.InvalidInputException;

public class AESCryptographerImpl implements Cryptographer {

	private static final byte[] keyValue = new byte[] { 'w', 'r', '+', 'i',
			'1', 'S', 'p', '2', 'O', 'u', 'F', 'a', '9', 'Q', '6', 'g' };
	private static final String ALGORITHM = "AES";

	@Override
	public String decrypt(String encryptedStr) throws CryptographyException,
			InvalidInputException {

		// validation
		if (encryptedStr == null) {
			throw new InvalidInputException("Invalid input. Value passes + "
					+ encryptedStr);
		}

		String decryptedValue = null;

		try {
			// generating secret key
			Key key = generateKey();
			Cipher c = Cipher.getInstance(ALGORITHM);
			c.init(Cipher.DECRYPT_MODE, key);

			byte[] decordedValue = new BASE64Decoder()
					.decodeBuffer(encryptedStr);
			byte[] decValue = c.doFinal(decordedValue);
			decryptedValue = new String(decValue);
		} catch (InvalidKeyException e) {
			e.printStackTrace();
			throw new CryptographyException(e.getMessage());
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			throw new CryptographyException(e.getMessage());
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
			throw new CryptographyException(e.getMessage());
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
			throw new CryptographyException(e.getMessage());
		} catch (BadPaddingException e) {
			e.printStackTrace();
			throw new CryptographyException(e.getMessage());
		} catch (IOException e) {
			e.printStackTrace();
			throw new CryptographyException(e.getMessage());
		}
		return decryptedValue;

	}

	@Override
	public String encrypt(String str) throws CryptographyException,
			InvalidInputException {

		// validation
		if (str == null) {
			throw new InvalidInputException("Invalid input. Value passes + "
					+ str);
		}

		String encryptedValue = null;

		try {

			// generating secret key
			Key key = generateKey();
			Cipher c = Cipher.getInstance(ALGORITHM);
			c.init(Cipher.ENCRYPT_MODE, key);

			// encrypting
			byte[] encValue = c.doFinal(str.getBytes());

			// encoding to be storable as string
			encryptedValue = new BASE64Encoder().encode(encValue);

		} catch (InvalidKeyException e) {
			e.printStackTrace();
			throw new CryptographyException(e.getMessage());
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			throw new CryptographyException(e.getMessage());
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
			throw new CryptographyException(e.getMessage());
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
			throw new CryptographyException(e.getMessage());
		} catch (BadPaddingException e) {
			e.printStackTrace();
			throw new CryptographyException(e.getMessage());
		}
		return encryptedValue;
	}

	private static Key generateKey() {

		Key key = new SecretKeySpec(keyValue, ALGORITHM);
		return key;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			System.out.println(new AESCryptographerImpl()
					.encrypt("System123$"));
		} catch (CryptographyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidInputException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		try {
			System.out.println(new AESCryptographerImpl()
					.decrypt("qGdVRgWvUgfMRAXBGHkAX7AhAlUp8cAIysnosUEuCdc="));
		} catch (CryptographyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidInputException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}
